#include <types.h>
#include <errno.h>
#include <tty.h>
#include <tty_ldisc.h>
#include <irq.h>
#include <init.h>

static struct tty_ldisc *tty_ldiscs[LDISCS_NR];

int tty_register_ldisc(unsigned int disc, struct tty_ldisc *new_ldisc)
{
    if ((disc >= LDISCS_NR) || !new_ldisc || tty_ldiscs[disc])
        return -EINVAL;

    tty_ldiscs[disc] = new_ldisc;
    return 0;
}

int tty_ldisc_open(struct tty *tty)
{
	int ret = 0;
	
	if (IS_ERR(tty) || (tty == NULL))
		return -EINVAL;
	
	if ((tty->ldisc_nr >= 0) && (tty->ldisc_nr < LDISCS_NR) && tty_flags_is_ldisc_closed(tty->flags)) {		
		if (tty_ldiscs[tty->ldisc_nr]->ops.open)
			ret = tty_ldiscs[tty->ldisc_nr]->ops.open(tty);
		if (!ret) {
			tty_flags_set_ldisc_opened(tty->flags);
			tty_flags_set_ldisc_enabled(tty->flags);
		}
	}

	return ret;	
}

void tty_ldisc_close(struct tty *tty)
{
	if (IS_ERR(tty) || (tty == NULL))
		return;

	if ((tty->ldisc_nr >= 0) && (tty->ldisc_nr < LDISCS_NR) && tty_flags_is_ldisc_opened(tty->flags)) {		
		if (tty_ldiscs[tty->ldisc_nr]->ops.close)
			tty_ldiscs[tty->ldisc_nr]->ops.close(tty);
		tty_flags_set_ldisc_disabled(tty->flags);
		tty_flags_set_ldisc_closed(tty->flags);
	}	
}

int tty_set_ldisc(struct tty *tty, int ldisc)
{
	unsigned int flags;
	int ret;

	if (IS_ERR(tty) || (tty == NULL) || (ldisc >= LDISCS_NR) || (ldisc == tty->ldisc_nr))
		return -EINVAL;

	local_irq_save(flags);
	if (tty_flags_is_ldisc_opened(tty->flags)) 
		tty_ldisc_close(tty);
	tty->ldisc_nr  = ldisc;
	ret = tty_ldisc_open(tty);
	local_irq_restore(flags);
	return ret;
}

struct tty_ldisc *tty_ldisc_get(struct tty *tty)
{
	if (IS_ERR(tty) || (tty == NULL))
		return ERR_PTR(-EINVAL);

	if ((tty->ldisc_nr >= 0) && (tty->ldisc_nr <= LDISCS_NR))
		return tty_ldiscs[tty->ldisc_nr];

	return NULL;
}

static int tty_ldisc_init(void)
{
	int i;

	for (i = 0; i < LDISCS_NR; i++)
		tty_ldiscs[i] = NULL;

	return 0;
}
core0_initcall(tty_ldisc_init);

